package cn.turboinfo.fuyang.api.gateway.admin.controller.portal;

import cn.turboinfo.fuyang.api.domain.admin.service.cms.CmsArticleService;
import cn.turboinfo.fuyang.api.domain.admin.service.cms.CmsCategoryService;
import cn.turboinfo.fuyang.api.domain.admin.service.cms.CmsMessageBoardService;
import cn.turboinfo.fuyang.api.domain.admin.usecase.category.CategoryTreeUseCase;
import cn.turboinfo.fuyang.api.domain.admin.usecase.company.HousekeepingCompanyAuthLabelListUseCase;
import cn.turboinfo.fuyang.api.domain.admin.usecase.knowledge.PortalKnowledgeBaseListUseCase;
import cn.turboinfo.fuyang.api.domain.admin.usecase.shop.HousekeepingShopPortalSearchUseCase;
import cn.turboinfo.fuyang.api.domain.admin.usecase.staff.PortalHousekeepingStaffViewUseCase;
import cn.turboinfo.fuyang.api.domain.common.service.file.FileAttachmentService;
import cn.turboinfo.fuyang.api.domain.common.service.product.ProductService;
import cn.turboinfo.fuyang.api.domain.common.service.stat.IndexStatService;
import cn.turboinfo.fuyang.api.domain.web.component.file.FileAttachmentHelper;
import cn.turboinfo.fuyang.api.domain.web.component.file.FileRefTypeConstant;
import cn.turboinfo.fuyang.api.entity.admin.enumeration.cms.CmsArticleStatus;
import cn.turboinfo.fuyang.api.entity.admin.pojo.captcha.Captcha;
import cn.turboinfo.fuyang.api.entity.admin.pojo.cms.*;
import cn.turboinfo.fuyang.api.entity.common.pojo.category.QCategory;
import cn.turboinfo.fuyang.api.entity.common.pojo.company.QHousekeepingCompany;
import cn.turboinfo.fuyang.api.entity.common.pojo.file.FileAttachment;
import cn.turboinfo.fuyang.api.entity.common.pojo.product.Product;
import cn.turboinfo.fuyang.api.gateway.admin.constant.AdminConstants;
import cn.turboinfo.fuyang.api.gateway.admin.controller.BaseController;
import cn.turboinfo.fuyang.api.gateway.admin.fo.cms.CmsMessageBoardCreateFO;
import cn.turboinfo.fuyang.api.gateway.admin.fo.company.HousekeepingCompanyAuthLabelFO;
import cn.turboinfo.fuyang.api.gateway.admin.fo.file.FileUploadFO;
import cn.turboinfo.fuyang.api.gateway.admin.fo.knowledge.PortalKnowledgeListFO;
import cn.turboinfo.fuyang.api.gateway.admin.fo.portal.*;
import cn.turboinfo.fuyang.api.gateway.admin.framework.http.fo.LimitDataFO;
import cn.turboinfo.fuyang.api.gateway.admin.framework.http.fo.LimitFO;
import cn.turboinfo.fuyang.api.gateway.admin.framework.http.fo.RestResponseFO;
import cn.turboinfo.fuyang.api.gateway.admin.framework.http.fo.SortFO;
import cn.turboinfo.fuyang.api.provider.admin.component.config.AdminKitConfig;
import cn.turboinfo.fuyang.api.provider.admin.component.session.AdminSessionHelper;
import com.github.houbb.heaven.util.util.CollectionUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import net.sunshow.toolkit.core.base.enums.YesNoStatus;
import net.sunshow.toolkit.core.qbean.api.request.QPage;
import net.sunshow.toolkit.core.qbean.api.request.QRequest;
import net.sunshow.toolkit.core.qbean.api.request.QSort;
import net.sunshow.toolkit.core.qbean.api.response.QResponse;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanCreatorHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanSearchHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QBeanUpdaterHelper;
import net.sunshow.toolkit.core.qbean.helper.component.request.QPageRequestHelper;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author gadzs
 * @description 门户网站接口
 * @date 2023/2/22 16:08
 */
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/portal")
public class PortalController extends BaseController {

    private final CmsArticleService cmsArticleService;
    private final CmsCategoryService cmsCategoryService;
    private final FileAttachmentHelper fileAttachmentHelper;
    private final CategoryTreeUseCase categoryTreeUseCase;
    private final AdminSessionHelper sessionHelper;
    private final CmsMessageBoardService cmsMessageBoardService;

    public final FileAttachmentService fileAttachmentService;

    private final AdminKitConfig adminKitConfig;

    private final HousekeepingCompanyAuthLabelListUseCase housekeepingCompanyAuthLabelListUseCase;

    private final IndexStatService indexStatService;

    private final HousekeepingShopPortalSearchUseCase housekeepingShopPortalSearchUseCase;

    private final ProductService productService;

    private final PortalHousekeepingStaffViewUseCase portalHousekeepingStaffViewUseCase;

    private final PortalKnowledgeBaseListUseCase portalKnowledgeBaseListUseCase;

    /**
     * 数据统计
     *
     * @return
     */
    @RequestMapping("/statData")
    public RestResponseFO statData() {
        val indexPortal = indexStatService.getStatPortalData();
        if (Objects.isNull(indexPortal)) {
            indexStatService.setStatPortalData();
            return RestResponseFO.ok(indexStatService.getStatPortalData());
        }
        return RestResponseFO.ok(indexPortal);
    }

    /**
     * 公司授权标签
     *
     * @return
     */
    @RequestMapping("/company/authLabel/all")
    public RestResponseFO authLabelAll() {
        val companyAuthLabelFOS = housekeepingCompanyAuthLabelListUseCase.execute(HousekeepingCompanyAuthLabelListUseCase.InputData
                .builder()
                .build()).getAuthLabelList().stream().map(housekeepingCompanyAuthLabel -> {
            HousekeepingCompanyAuthLabelFO housekeepingCompanyAuthLabelFO = HousekeepingCompanyAuthLabelFO.builder()
                    .id(housekeepingCompanyAuthLabel.getId()).name(housekeepingCompanyAuthLabel.getName()).build();
            return housekeepingCompanyAuthLabelFO;
        }).collect(Collectors.toList());
        return RestResponseFO.ok(companyAuthLabelFOS);
    }

    /**
     * 公司店铺检索
     *
     * @param search
     * @param page
     * @param sort
     * @return
     */
    @RequestMapping("/shop/search")
    public RestResponseFO shopSearch(@Valid PortalShopSearchFO search, LimitFO page, SortFO sort) {
        if (StringUtils.isNotBlank(search.getAreaCode()) && search.getAreaCode().equals("0")) {
            search.setAreaCode(null);
        }
        QRequest request = QBeanSearchHelper.convertQRequest(search);
        if (Objects.nonNull(search.getCategoryId())) {
            List<Object> shopIdList = productService.findByCategoryId(search.getCategoryId()).stream().map(Product::getShopId).collect(Collectors.toList());
            request.filterIn("id", shopIdList);
        }
        if (Objects.nonNull(search.getKeyword())) {
            List<Object> shopIdList = productService.findByNameContaining(search.getKeyword()).stream().map(Product::getShopId).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(shopIdList)) {
                request.filterIn("id", shopIdList);
            } else {
                request.filterLike("name", "%" + search.getKeyword() + "%");
            }
        }
        if (sort.getSortFields() == null) {
            sort.setSortFields(new String[]{QHousekeepingCompany.id + "|" + QSort.Order.DESC.name()});
        }
        HousekeepingShopPortalSearchUseCase.OutputData outputData = housekeepingShopPortalSearchUseCase.execute(HousekeepingShopPortalSearchUseCase.InputData
                .builder()
                .request(request)
                .requestPage(page.toQPage(sort.toQSortList()))
                .build());
        return RestResponseFO.ok(LimitDataFO.fromQResponse(outputData.getQResponse()));
    }


    /**
     * 服务栏目
     *
     * @return
     */
    @RequestMapping("/service/category")
    public RestResponseFO serviceCategory() {
        return RestResponseFO.ok(categoryTreeUseCase.execute(CategoryTreeUseCase.InputData.builder().build()).getCategoryList());
    }

    /**
     * 栏目列表
     *
     * @param search
     * @param sort
     * @return
     */
    @RequestMapping("/category/list")
    public RestResponseFO categoryList(PortalCategorySearchFO search, SortFO sort) {
        if (StringUtils.isNotEmpty(search.getParentCode())) {
            CmsCategory category = cmsCategoryService.getCategoryByCode(search.getParentCode());
            if (category != null) {
                search.setParentId(category.getId());
            }
        }
        if (search.getParentId() == null) {
            return RestResponseFO.ok();
        }
        QRequest request = QBeanSearchHelper.convertQRequest(search);
        if (sort.getSortFields() == null) {
            sort.setSortFields(new String[]{QCmsCategory.sort + "|" + QSort.Order.DESC.name(), QCmsCategory.id + "|" + QSort.Order.DESC.name()});
        }
        List<CmsCategory> categoryList = QPageRequestHelper.request(request, QPage.newInstance()
                .addOrder(QCmsCategory.sort, QSort.Order.DESC)
                .addOrder(QCmsCategory.id, QSort.Order.DESC), cmsCategoryService::findAll);
        return RestResponseFO.ok(categoryList);
    }

    /**
     * 根据code获取栏目
     *
     * @param code
     * @return
     */
    @RequestMapping("/category/{code}")
    public RestResponseFO categoryByCode(@PathVariable("code") String code) {
        CmsCategory category = cmsCategoryService.getCategoryByCode(code);
        return RestResponseFO.ok(category);
    }

    /**
     * 文章列表
     *
     * @param search
     * @param page
     * @param sort
     * @return
     */
    @RequestMapping("/article/list")
    public RestResponseFO articleList(PortalArticleSearchFO search, LimitFO page, SortFO sort) {
        checkLimit(page);
        if (search.getCategoryId() == null && StringUtils.isEmpty(search.getCategoryCodes())) {
            return RestResponseFO.ok();
        }
        QRequest request = QBeanSearchHelper.convertQRequest(search);
        request.filterEqual("status", CmsArticleStatus.PUBLISHED.getValue());
        if (sort.getSortFields() == null) {
            sort.setSortFields(new String[]{QCmsArticle.sort + "|" + QSort.Order.DESC.name(), QCmsArticle.publishedTime + "|" + QSort.Order.DESC.name()});
        }

        // 默认获取所有分类列表
        List<CmsCategory> categoryList = QPageRequestHelper.request(QRequest.newInstance(), QPage.newInstance().addOrder(QCmsArticle.id), cmsCategoryService::findAll);

        if (StringUtils.isNotEmpty(search.getCategoryCodes())) {
            List<Object> categoryIds = Lists.newArrayList();
            categoryList.forEach(category -> {
                if (search.getCategoryCodes().contains(category.getCode())) {
                    categoryIds.add(category.getId());
                }
            });
            if (categoryIds.size() > 0) {
                request.filterIn("categoryId", categoryIds);
            }
        }

        QResponse<CmsArticle> qresponse = cmsArticleService.findAll(request, page.toQPage(sort.toQSortList()));
        List<PortalArticleDisplayFO> foList = qresponse.getPagedData().stream().map(article -> {
            PortalArticleDisplayFO fo = new PortalArticleDisplayFO();
            BeanUtils.copyProperties(article, fo);
            return fo;
        }).collect(Collectors.toList());
        QResponse<PortalArticleDisplayFO> foqresponse = new QResponse<>(qresponse.getPage(), qresponse.getPageSize(), foList, qresponse.getTotal());
        return RestResponseFO.ok(LimitDataFO.fromQResponse(foqresponse));
    }

    /**
     * 文章详情
     *
     * @param id
     * @return
     */
    @GetMapping("/article/{id}")
    public RestResponseFO getArticle(@PathVariable("id") Long id) {
        Optional<CmsArticle> articleOptional = cmsArticleService.getById(id);
        if (!articleOptional.isPresent() || articleOptional.get().getStatus().getValue() != CmsArticleStatus.PUBLISHED.getValue()) {
            return RestResponseFO.ok(null);
        }
        CmsArticle article = articleOptional.get();
        addVisitCount(article);
        Optional<CmsCategory> category = cmsCategoryService.getById(article.getCategoryId());
        if (category.isPresent()) {
            article.setCategoryName(category.get().getName());
        }
        return RestResponseFO.ok(article);
    }

    /**
     * 上传附件
     *
     * @param fo
     * @return
     * @throws IOException
     */
    @PostMapping("/uploadFile")
    @ResponseBody
    public RestResponseFO uploadFile(FileUploadFO fo) throws IOException {
        if (FileRefTypeConstant.noAuthRefTypeUpload.contains(fo.getRefType())) {
            FileAttachment fileAttachment = fileAttachmentHelper.checkAndSaveFileAttachment(fo.getFile(), StringUtils.split(adminKitConfig.getAttachmentAllowExt(), "|"), fo.getRelativePath(), fo.getRefType(), null, false);
            return RestResponseFO.ok(fileAttachment.getId());
        }
        return RestResponseFO.error("不支持的附件类型");
    }


    /**
     * 附件下载
     *
     * @param fileAttachmentId
     * @param response
     * @throws IOException
     */
    @GetMapping("/downloadAttachment/{articleId}/{fileAttachmentId}")
    public void downloadAttachment(@PathVariable Long articleId, @PathVariable Long fileAttachmentId, HttpServletResponse response) throws IOException {
        Optional<FileAttachment> fileAttachmentOptional = fileAttachmentService.getById(fileAttachmentId);
        if (fileAttachmentOptional.isPresent() && FileRefTypeConstant.noAuthRefTypeView.contains(fileAttachmentOptional.get().getRefType())) {
            File file = fileAttachmentHelper.readFileAttachment(fileAttachmentOptional.get());

            Boolean donwnloadFlag = Boolean.TRUE;
            CmsArticle article = null;
            if (!articleId.equals(0L)) {//记录下载次数
                Optional<CmsArticle> articleOptional = cmsArticleService.getById(articleId);
                if (!articleOptional.isPresent() || articleOptional.get().getStatus().getValue() != CmsArticleStatus.PUBLISHED.getValue()) {
                    donwnloadFlag = Boolean.FALSE;
                } else {
                    article = articleOptional.get();
                }
            }

            // 如果文件名存在，则进行下载
            if (donwnloadFlag && file.exists()) {
                // 实现文件下载
                try (InputStream is = new FileInputStream(file);
                     OutputStream os = response.getOutputStream()) {
                    // 配置文件下载
                    response.setContentType("application/octet-stream");
                    // 下载文件能正常显示中文
                    response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + URLEncoder.encode(article.getTitle(), "UTF-8"));
                    IOUtils.copy(is, os);
                    os.flush();
                }
            }
        }
    }

    /**
     * 查看图片
     *
     * @param fileAttachmentId
     * @param response
     * @throws IOException
     */
    @GetMapping("/viewImage/{fileAttachmentId}")
    public void viewImage(@PathVariable Long fileAttachmentId, HttpServletResponse response) throws IOException {
        Optional<FileAttachment> fileAttachmentOptional = fileAttachmentService.getById(fileAttachmentId);
        if (fileAttachmentOptional.isPresent() && FileRefTypeConstant.noAuthRefTypeView.contains(fileAttachmentOptional.get().getRefType())) {
            viewAttachmentImage(fileAttachmentOptional.get(), response);
        }
    }

    /**
     * 留言板列表
     *
     * @param search
     * @param page
     * @param sort
     * @return
     */
    @RequestMapping("/messageboard/list")
    public RestResponseFO messageboardList(PortalMessageBoardSearchFO search, LimitFO page, SortFO sort) {
        checkLimit(page);
        QRequest request = QBeanSearchHelper.convertQRequest(search);
        request.filterEqual("isShow", YesNoStatus.YES.getValue());
        if (sort.getSortFields() == null) {
            sort.setSortFields(new String[]{QCmsMessageBoard.sort + "|" + QSort.Order.DESC.name(), QCategory.id + "|" + QSort.Order.DESC.name()});
        }
        QResponse<CmsMessageBoard> qresponse = cmsMessageBoardService.findAll(request, page.toQPage(sort.toQSortList()));
        return RestResponseFO.ok(LimitDataFO.fromQResponse(qresponse));
    }

    /**
     * 添加留言
     *
     * @param fo
     * @return
     */
    @PostMapping("/messageboard/create")
    public RestResponseFO messageboardCreate(HttpServletRequest request, @RequestBody @Valid CmsMessageBoardCreateFO fo) {
        Captcha captchaInSession = (Captcha) request.getSession().getAttribute(AdminConstants.AttrDefaultCaptcha);
        if (captchaInSession == null || !captchaInSession.getCode().equals(fo.getCaptcha())) {
            return RestResponseFO.error("验证码不正确");
        }
        if (captchaInSession.isExpired()) {
            return RestResponseFO.error("验证码已过期");
        }

        CmsMessageBoardCreator.Builder builder = CmsMessageBoardCreator.builder();
        builder.withUserId(0L);
        builder.withUsername("");
        builder.withIsShow(YesNoStatus.NO);
        builder.withSort(0);
        if (fo.getAdviceId() == null) {
            builder.withAdviceId(0L);
        }
        QBeanCreatorHelper.copyPropertiesToCreatorBuilder(builder, CmsMessageBoardCreator.class, fo);
        CmsMessageBoard messageBoard = cmsMessageBoardService.save(builder.build());
        return RestResponseFO.ok(messageBoard.getId());
    }

    /**
     * 添加访问记录
     *
     * @param article
     */
    private void addVisitCount(CmsArticle article) {
        article.setVisitCount(article.getVisitCount() + 1);
        CmsArticleUpdater.Builder builder = CmsArticleUpdater.builder(article.getId());
        QBeanUpdaterHelper.copyPropertiesToUpdateBuilder(builder, CmsArticleUpdater.class, article);
        cmsArticleService.update(builder.build());
    }

    @PostMapping("/staff/search")
    public RestResponseFO searchStaff(@RequestBody @Valid PortalStaffSearchFO fo) {

        PortalHousekeepingStaffViewUseCase.OutputData outputData = portalHousekeepingStaffViewUseCase.execute(PortalHousekeepingStaffViewUseCase.InputData
                .builder()
                .code(fo.getCode())
                .build());
        return RestResponseFO.ok(outputData.getStaff());
    }

    @PostMapping("/knowledge/search")
    public RestResponseFO searchKnowledge(@RequestBody @Valid PortalKnowledgeListFO fo) {

        PortalKnowledgeBaseListUseCase.OutputData outputData = portalKnowledgeBaseListUseCase.execute(PortalKnowledgeBaseListUseCase.InputData
                .builder()
                .type(fo.getType())
                .build());
        return RestResponseFO.ok(outputData.getKnowledgeList());
    }
}
